package com.acme.service.bank.impl;

import com.acme.domain.account.Account;
import com.acme.domain.account.CheckingAccount;
import com.acme.domain.account.SavingAccount;
import com.acme.domain.bank.Client;
import com.acme.domain.bank.Gender;
import com.acme.exceptions.BrokenBankFeedFileException;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BankFeedDataReader {
    private static final Pattern keyValuePattern = Pattern.compile("(\\w*)=(.*?);");
    private static final String ACCOUNT_TYPE_KEY = "accounttype";
    private static final String CHECKING_ACCOUNT_VALUE_KEY = "c";
    private static final String SAVING_ACCOUNT_VALUE_KEY = "s";
    private static final String BALANCE_KEY = "balance";
    private static final String OVERDRAFT_KEY = "overdraft";
    private static final String NAME_KEY = "name";
    private static final String GENDER_KEY = "gender";
    private static final String MALE_VALUE_KEY = "m";
    private static final String FEMALE_VALUE_KEY = "f";

    public Client readClientLine(String line) throws BrokenBankFeedFileException {
        ClientFeedDataLine clientFeedDataLine = getBankFeedDataLine(line);
        Client client = clientFeedDataLine.constructClient();
        Account account = clientFeedDataLine.constructAccount();
        client.addAccount(account);
        return client;
    }

    private ClientFeedDataLine getBankFeedDataLine(String clientDataLine) throws BrokenBankFeedFileException {
        Matcher matcher = keyValuePattern.matcher(clientDataLine);
        ClientFeedDataLine clientFeedDataLine = new ClientFeedDataLine();
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = matcher.group(2);
            switch (key) {
                case ACCOUNT_TYPE_KEY:
                    clientFeedDataLine.accountTypeString = value;
                    break;
                case BALANCE_KEY:
                    clientFeedDataLine.balance = new BigDecimal(value);
                    break;
                case OVERDRAFT_KEY:
                    clientFeedDataLine.overdraft = new BigDecimal(value);
                    break;
                case NAME_KEY:
                    clientFeedDataLine.name = value;
                    break;
                case GENDER_KEY:
                    switch (value) {
                        case MALE_VALUE_KEY:
                            clientFeedDataLine.gender = Gender.MALE;
                            break;
                        case FEMALE_VALUE_KEY:
                            clientFeedDataLine.gender = Gender.FEMALE;
                            break;
                        default:
                            throw new BrokenBankFeedFileException();
                    }
                    break;
                default:
                    throw new BrokenBankFeedFileException();
            }
        }
        return clientFeedDataLine;
    }

    private class ClientFeedDataLine {
        public String accountTypeString;
        public BigDecimal balance;
        public BigDecimal overdraft;
        public String name;
        public Gender gender;

        private Client constructClient() throws BrokenBankFeedFileException {
            if (canCreateClient()) {
                return new Client(name, gender);
            } else {
                throw new BrokenBankFeedFileException();
            }
        }

        private boolean canCreateClient() {
            return name != null && gender != null;
        }

        private Account constructAccount() throws BrokenBankFeedFileException {
            if (canCreateAccount()) {
                if (accountTypeString.equals(SAVING_ACCOUNT_VALUE_KEY)) {
                    return new SavingAccount(balance);
                } else {
                    return new CheckingAccount(balance, overdraft);
                }
            } else {
                throw new BrokenBankFeedFileException();
            }
        }

        private boolean canCreateAccount() {
            return accountTypeString != null && balance != null &&
                    (accountTypeString.equals(SAVING_ACCOUNT_VALUE_KEY) ||
                            (accountTypeString.equals(CHECKING_ACCOUNT_VALUE_KEY) && overdraft != null));
        }
    }
}
